"""FileUtils类 - 提供文件操作相关的实用功能"""

import os
import shutil
import json
import yaml
import csv
import pandas as pd
from typing import List, Dict, Any, Union, Optional

from rpa_framework.rpa_logging.logger import Logger
from rpa_framework.exceptions.rpa_exceptions import RPAException as FileOperationException, FileNotFoundException

class FileUtils:
    """文件工具类，提供文件读写、复制、移动、删除等操作"""
    
    def __init__(self):
        """初始化文件工具类"""
        self.logger = Logger("FileUtils")
    
    def read_text_file(self, file_path: str, encoding: str = "utf-8") -> str:
        """读取文本文件
        
        Args:
            file_path: 文件路径
            encoding: 文件编码
            
        Returns:
            文件内容
            
        Raises:
            FileNotFoundException: 文件不存在
            FileOperationException: 文件读取失败
        """
        self.logger.debug(f"读取文本文件: {file_path}")
        
        if not os.path.exists(file_path):
            raise FileNotFoundException(f"文件不存在: {file_path}")
        
        try:
            with open(file_path, 'r', encoding=encoding) as f:
                return f.read()
        except Exception as e:
            self.logger.error(f"读取文件失败: {str(e)}")
            raise FileOperationException(f"无法读取文件: {str(e)}") from e
    
    def write_text_file(self, file_path: str, content: str, encoding: str = "utf-8", append: bool = False) -> None:
        """写入文本文件
        
        Args:
            file_path: 文件路径
            content: 要写入的内容
            encoding: 文件编码
            append: 是否追加模式
            
        Raises:
            FileOperationException: 文件写入失败
        """
        self.logger.debug(f"{'追加' if append else '写入'}文本文件: {file_path}")
        
        # 确保目录存在
        directory = os.path.dirname(file_path)
        if directory and not os.path.exists(directory):
            os.makedirs(directory)
        
        try:
            mode = 'a' if append else 'w'
            with open(file_path, mode, encoding=encoding) as f:
                f.write(content)
        except Exception as e:
            self.logger.error(f"写入文件失败: {str(e)}")
            raise FileOperationException(f"无法写入文件: {str(e)}") from e
    
    def read_json_file(self, file_path: str, encoding: str = "utf-8") -> Dict[str, Any]:
        """读取JSON文件
        
        Args:
            file_path: 文件路径
            encoding: 文件编码
            
        Returns:
            JSON数据（字典或列表）
            
        Raises:
            FileNotFoundException: 文件不存在
            FileOperationException: 文件读取失败
        """
        self.logger.debug(f"读取JSON文件: {file_path}")
        
        if not os.path.exists(file_path):
            raise FileNotFoundException(f"文件不存在: {file_path}")
        
        try:
            with open(file_path, 'r', encoding=encoding) as f:
                return json.load(f)
        except json.JSONDecodeError as e:
            self.logger.error(f"JSON解析失败: {str(e)}")
            raise FileOperationException(f"JSON格式错误: {str(e)}") from e
        except Exception as e:
            self.logger.error(f"读取JSON文件失败: {str(e)}")
            raise FileOperationException(f"无法读取JSON文件: {str(e)}") from e
    
    def write_json_file(self, file_path: str, data: Any, encoding: str = "utf-8", indent: int = 2) -> None:
        """写入JSON文件
        
        Args:
            file_path: 文件路径
            data: 要写入的数据
            encoding: 文件编码
            indent: 缩进空格数
            
        Raises:
            FileOperationException: 文件写入失败
        """
        self.logger.debug(f"写入JSON文件: {file_path}")
        
        # 确保目录存在
        directory = os.path.dirname(file_path)
        if directory and not os.path.exists(directory):
            os.makedirs(directory)
        
        try:
            with open(file_path, 'w', encoding=encoding) as f:
                json.dump(data, f, ensure_ascii=False, indent=indent)
        except Exception as e:
            self.logger.error(f"写入JSON文件失败: {str(e)}")
            raise FileOperationException(f"无法写入JSON文件: {str(e)}") from e
    
    def read_yaml_file(self, file_path: str, encoding: str = "utf-8") -> Dict[str, Any]:
        """读取YAML文件
        
        Args:
            file_path: 文件路径
            encoding: 文件编码
            
        Returns:
            YAML数据
            
        Raises:
            FileNotFoundException: 文件不存在
            FileOperationException: 文件读取失败
        """
        self.logger.debug(f"读取YAML文件: {file_path}")
        
        if not os.path.exists(file_path):
            raise FileNotFoundException(f"文件不存在: {file_path}")
        
        try:
            with open(file_path, 'r', encoding=encoding) as f:
                return yaml.safe_load(f)
        except Exception as e:
            self.logger.error(f"读取YAML文件失败: {str(e)}")
            raise FileOperationException(f"无法读取YAML文件: {str(e)}") from e
    
    def write_yaml_file(self, file_path: str, data: Any, encoding: str = "utf-8") -> None:
        """写入YAML文件
        
        Args:
            file_path: 文件路径
            data: 要写入的数据
            encoding: 文件编码
            
        Raises:
            FileOperationException: 文件写入失败
        """
        self.logger.debug(f"写入YAML文件: {file_path}")
        
        # 确保目录存在
        directory = os.path.dirname(file_path)
        if directory and not os.path.exists(directory):
            os.makedirs(directory)
        
        try:
            with open(file_path, 'w', encoding=encoding) as f:
                yaml.dump(data, f, default_flow_style=False, allow_unicode=True)
        except Exception as e:
            self.logger.error(f"写入YAML文件失败: {str(e)}")
            raise FileOperationException(f"无法写入YAML文件: {str(e)}") from e
    
    def read_csv_file(self, file_path: str, encoding: str = "utf-8") -> List[Dict[str, Any]]:
        """读取CSV文件
        
        Args:
            file_path: 文件路径
            encoding: 文件编码
            
        Returns:
            CSV数据列表
            
        Raises:
            FileNotFoundException: 文件不存在
            FileOperationException: 文件读取失败
        """
        self.logger.debug(f"读取CSV文件: {file_path}")
        
        if not os.path.exists(file_path):
            raise FileNotFoundException(f"文件不存在: {file_path}")
        
        try:
            result = []
            with open(file_path, 'r', encoding=encoding, newline='') as f:
                reader = csv.DictReader(f)
                for row in reader:
                    result.append(dict(row))
            return result
        except Exception as e:
            self.logger.error(f"读取CSV文件失败: {str(e)}")
            raise FileOperationException(f"无法读取CSV文件: {str(e)}") from e
    
    def write_csv_file(self, file_path: str, data: List[Dict[str, Any]], encoding: str = "utf-8") -> None:
        """写入CSV文件
        
        Args:
            file_path: 文件路径
            data: 要写入的数据列表
            encoding: 文件编码
            
        Raises:
            FileOperationException: 文件写入失败
        """
        self.logger.debug(f"写入CSV文件: {file_path}")
        
        if not data:
            return
        
        # 确保目录存在
        directory = os.path.dirname(file_path)
        if directory and not os.path.exists(directory):
            os.makedirs(directory)
        
        try:
            fieldnames = data[0].keys()
            with open(file_path, 'w', encoding=encoding, newline='') as f:
                writer = csv.DictWriter(f, fieldnames=fieldnames)
                writer.writeheader()
                writer.writerows(data)
        except Exception as e:
            self.logger.error(f"写入CSV文件失败: {str(e)}")
            raise FileOperationException(f"无法写入CSV文件: {str(e)}") from e
    
    def copy_file(self, source: str, destination: str, overwrite: bool = True) -> None:
        """复制文件
        
        Args:
            source: 源文件路径
            destination: 目标文件路径
            overwrite: 是否覆盖已存在的文件
            
        Raises:
            FileNotFoundException: 源文件不存在
            FileOperationException: 文件复制失败
        """
        self.logger.debug(f"复制文件: {source} -> {destination}")
        
        if not os.path.exists(source):
            raise FileNotFoundException(f"源文件不存在: {source}")
        
        if os.path.exists(destination) and not overwrite:
            self.logger.warning(f"目标文件已存在且不覆盖: {destination}")
            return
        
        # 确保目标目录存在
        dest_dir = os.path.dirname(destination)
        if dest_dir and not os.path.exists(dest_dir):
            os.makedirs(dest_dir)
        
        try:
            shutil.copy2(source, destination)
        except Exception as e:
            self.logger.error(f"复制文件失败: {str(e)}")
            raise FileOperationException(f"无法复制文件: {str(e)}") from e
    
    def move_file(self, source: str, destination: str, overwrite: bool = True) -> None:
        """移动文件
        
        Args:
            source: 源文件路径
            destination: 目标文件路径
            overwrite: 是否覆盖已存在的文件
            
        Raises:
            FileNotFoundException: 源文件不存在
            FileOperationException: 文件移动失败
        """
        self.logger.debug(f"移动文件: {source} -> {destination}")
        
        if not os.path.exists(source):
            raise FileNotFoundException(f"源文件不存在: {source}")
        
        if os.path.exists(destination):
            if overwrite:
                os.remove(destination)
            else:
                self.logger.warning(f"目标文件已存在且不覆盖: {destination}")
                return
        
        # 确保目标目录存在
        dest_dir = os.path.dirname(destination)
        if dest_dir and not os.path.exists(dest_dir):
            os.makedirs(dest_dir)
        
        try:
            shutil.move(source, destination)
        except Exception as e:
            self.logger.error(f"移动文件失败: {str(e)}")
            raise FileOperationException(f"无法移动文件: {str(e)}") from e
    
    def delete_file(self, file_path: str) -> bool:
        """删除文件
        
        Args:
            file_path: 文件路径
            
        Returns:
            成功删除返回True，文件不存在返回False
            
        Raises:
            FileOperationException: 文件删除失败
        """
        self.logger.debug(f"删除文件: {file_path}")
        
        if not os.path.exists(file_path):
            self.logger.warning(f"文件不存在: {file_path}")
            return False
        
        try:
            os.remove(file_path)
            return True
        except Exception as e:
            self.logger.error(f"删除文件失败: {str(e)}")
            raise FileOperationException(f"无法删除文件: {str(e)}") from e
    
    def list_files(self, directory: str, pattern: str = None, recursive: bool = False) -> List[str]:
        """列出目录中的文件
        
        Args:
            directory: 目录路径
            pattern: 文件匹配模式（使用glob格式）
            recursive: 是否递归查找
            
        Returns:
            文件路径列表
            
        Raises:
            FileOperationException: 目录访问失败
        """
        self.logger.debug(f"列出目录文件: {directory}, 模式: {pattern}, 递归: {recursive}")
        
        if not os.path.exists(directory):
            raise FileOperationException(f"目录不存在: {directory}")
        
        if not os.path.isdir(directory):
            raise FileOperationException(f"不是目录: {directory}")
        
        try:
            import glob
            
            if pattern:
                search_pattern = os.path.join(directory, "**" if recursive else "", pattern)
                return glob.glob(search_pattern, recursive=recursive)
            else:
                # 返回所有文件
                result = []
                if recursive:
                    for root, dirs, files in os.walk(directory):
                        for file in files:
                            result.append(os.path.join(root, file))
                else:
                    for item in os.listdir(directory):
                        item_path = os.path.join(directory, item)
                        if os.path.isfile(item_path):
                            result.append(item_path)
                return result
        except Exception as e:
            self.logger.error(f"列出文件失败: {str(e)}")
            raise FileOperationException(f"无法列出文件: {str(e)}") from e
    
    def create_directory(self, directory: str, parents: bool = True, exist_ok: bool = True) -> None:
        """创建目录
        
        Args:
            directory: 目录路径
            parents: 是否创建父目录
            exist_ok: 目录已存在是否抛出异常
        """
        self.logger.debug(f"创建目录: {directory}")
        
        try:
            os.makedirs(directory, exist_ok=exist_ok)
        except Exception as e:
            self.logger.error(f"创建目录失败: {str(e)}")
            raise FileOperationException(f"无法创建目录: {str(e)}") from e
    
    def delete_directory(self, directory: str, recursive: bool = True) -> bool:
        """删除目录
        
        Args:
            directory: 目录路径
            recursive: 是否递归删除
            
        Returns:
            成功删除返回True，目录不存在返回False
            
        Raises:
            FileOperationException: 目录删除失败
        """
        self.logger.debug(f"删除目录: {directory}, 递归: {recursive}")
        
        if not os.path.exists(directory):
            self.logger.warning(f"目录不存在: {directory}")
            return False
        
        try:
            if recursive:
                shutil.rmtree(directory)
            else:
                os.rmdir(directory)
            return True
        except Exception as e:
            self.logger.error(f"删除目录失败: {str(e)}")
            raise FileOperationException(f"无法删除目录: {str(e)}") from e
    
    def file_exists(self, file_path: str) -> bool:
        """检查文件是否存在
        
        Args:
            file_path: 文件路径
            
        Returns:
            文件存在返回True，否则返回False
        """
        return os.path.isfile(file_path)
    
    def directory_exists(self, directory_path: str) -> bool:
        """检查目录是否存在
        
        Args:
            directory_path: 目录路径
            
        Returns:
            目录存在返回True，否则返回False
        """
        return os.path.isdir(directory_path)
    
    def get_file_size(self, file_path: str) -> int:
        """获取文件大小
        
        Args:
            file_path: 文件路径
            
        Returns:
            文件大小（字节）
            
        Raises:
            FileNotFoundException: 文件不存在
        """
        if not os.path.exists(file_path):
            raise FileNotFoundException(f"文件不存在: {file_path}")
        
        return os.path.getsize(file_path)
    
    def read_excel_file(self, file_path: str, sheet_name: str = 0) -> pd.DataFrame:
        """读取Excel文件
        
        Args:
            file_path: 文件路径
            sheet_name: 工作表名称或索引
            
        Returns:
            DataFrame对象
            
        Raises:
            FileNotFoundException: 文件不存在
            FileOperationException: 文件读取失败
        """
        self.logger.debug(f"读取Excel文件: {file_path}, 工作表: {sheet_name}")
        
        if not os.path.exists(file_path):
            raise FileNotFoundException(f"文件不存在: {file_path}")
        
        try:
            return pd.read_excel(file_path, sheet_name=sheet_name)
        except Exception as e:
            self.logger.error(f"读取Excel文件失败: {str(e)}")
            raise FileOperationException(f"无法读取Excel文件: {str(e)}") from e
    
    def write_excel_file(self, file_path: str, data: Union[pd.DataFrame, Dict[str, pd.DataFrame]], sheet_name: str = "Sheet1") -> None:
        """写入Excel文件
        
        Args:
            file_path: 文件路径
            data: DataFrame对象或字典
            sheet_name: 工作表名称（当data为单个DataFrame时）
            
        Raises:
            FileOperationException: 文件写入失败
        """
        self.logger.debug(f"写入Excel文件: {file_path}")
        
        # 确保目录存在
        directory = os.path.dirname(file_path)
        if directory and not os.path.exists(directory):
            os.makedirs(directory)
        
        try:
            with pd.ExcelWriter(file_path, engine='xlsxwriter') as writer:
                if isinstance(data, pd.DataFrame):
                    data.to_excel(writer, sheet_name=sheet_name, index=False)
                elif isinstance(data, dict):
                    for sheet, df in data.items():
                        df.to_excel(writer, sheet_name=sheet, index=False)
        except Exception as e:
            self.logger.error(f"写入Excel文件失败: {str(e)}")
            raise FileOperationException(f"无法写入Excel文件: {str(e)}") from e

if __name__ == "__main__":
    # 示例用法
    file_utils = FileUtils()
    # 创建测试目录
    file_utils.create_directory("test_dir")
    # 写入测试文件
    file_utils.write_text_file("test_dir/test.txt", "Hello, RPA!")
    # 读取文件内容
    content = file_utils.read_text_file("test_dir/test.txt")
    print(f"文件内容: {content}")
    # 写入JSON文件
    file_utils.write_json_file("test_dir/test.json", {"name": "RPA", "version": "1.0"})
    # 读取JSON文件
    json_data = file_utils.read_json_file("test_dir/test.json")
    print(f"JSON数据: {json_data}")
    # 列出文件
    files = file_utils.list_files("test_dir")
    print(f"目录文件: {files}")
    # 清理
    file_utils.delete_directory("test_dir")